package edu.hit.pact.pgse.ch;
/**
 *
 * Copyright Pact Lab of H.I.T.
 *
 * Designed and Implemented by Grid Researching Group, 
 * Pact Lab, Harbin
 * 
 * This Project is part of the national 973 Project:
 * Internet Based Virtual Computing Environment
 *
 * http://pact518.hit.edu.cn
 * 
 * Author:       Meteor <meteorlxk@gmail.com> 
 * Copyright:    pact518 
 * Version:      1.0
 * Created:      2009-3-30 
 * LastModified: 2009-3-30
 */
import java.util.Collection;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentSkipListMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * @author Meteor
 * 
 */
public class ConsistentHash<T> {

	private final HashFunction hashFunction;
	private final Log logger;
	private final int numberOfReplicas;
	private final SortedMap<Integer, T> circle;

	public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Log logger) {
		this.hashFunction = hashFunction;
		this.numberOfReplicas = numberOfReplicas;
		this.circle = new ConcurrentSkipListMap<Integer, T>();
		this.logger = logger;
	}
	
	public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<T> nodes) {
		this.hashFunction = hashFunction;
		this.numberOfReplicas = numberOfReplicas;
		this.circle = new ConcurrentSkipListMap<Integer, T>();
		this.logger = LogFactory.getLog(ConsistentHash.class);
		for (T node : nodes) {
			add(node);
		}//end for
	}

	public void add(T node) {
		logger.error(node + " has been added into consistent hash table");
		for (int i = 0; i < numberOfReplicas; i++) {
			circle.put(hashFunction.hash(node.toString() + i), node);
		}//end for
	}

	public void remove(T node) {
		logger.error(node + " has been removed into consistent hash table");
		for (int i = 0; i < numberOfReplicas; i++) {
			circle.remove(hashFunction.hash(node.toString() + i));
		}//end for
	}

	public T get(Object key) {
		if (circle.isEmpty()) {
			logger.error("consistent hash table is null!");
			return null;
		}//end if
		
		logger.info("consistent hash table is not null, so null should not be returned");
		int hash = hashFunction.hash(key);
		if (!circle.containsKey(hash)) {
			SortedMap<Integer, T> tailMap = circle.tailMap(hash);
			hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
		}//end if
		return circle.get(hash);
	} 
}
